﻿using ECommon.Helper;
using EOrm.Helper;
using System;
using System.Collections.Generic;
using System.IO;

namespace EOrm
{
    /// <summary>
    /// 数据库连接控制类
    /// </summary>
    public class EOrmCon
    {
        /// <summary>
        /// 获取连接字符串实体
        /// </summary>
        /// <param name="key">目标配置</param>
        /// <param name="configPath">数据库配置文件路径</param>
        /// <returns>连接字符串实体</returns>
        public static ConStr GetConStr(string key = "default", string configPath = null)
        {
            if (_conStrList.Count <= 0)
            {
                _conStrList = GetConStrList(configPath);
            }
            if (string.IsNullOrEmpty(key))
            {
                key = "default";
            }
            ConStr target = null;
            if (_conStrList.Count > 0)
            {
                target = _conStrList.Find(a => a.ConKey == key);
            }
            if (target == null)
            {
                target = new ConStr();
                target.ConKey = key;
                _conStrList.Add(target);
                UpdateConStrList(_conStrList);
            }
            return target;
        }

        /// <summary>
        /// 获取连接字符串
        /// </summary>
        /// <param name="key">目标配置</param>
        /// <param name="configPath">数据库配置文件路径</param>
        /// <returns>连接字符串</returns>
        public static string GetConString(string key = "default", string configPath = null)
        {
            ConStr target = GetConStr(key, configPath);
            string res = CreateConStr(target);
            return res;
        }

        /// <summary>
        /// 设置连接字符串
        /// </summary>
        /// <param name="model">实体</param>
        public static void SetConStr(ConStr model)
        {
            bool isExist = _conStrList.Exists(a => a.ConKey == model.ConKey);
            if (isExist)
            {
                ConStr currentConStr = _conStrList.Find(a => a.ConKey == model.ConKey);
                currentConStr.Instance = model.Instance;
                currentConStr.SysTrusted = model.SysTrusted;
                currentConStr.User = model.User;
                currentConStr.Version = model.Version;
                currentConStr.Category = model.Category;
                currentConStr.DbName = model.DbName;
                currentConStr.FilePath = model.FilePath;
                currentConStr.Host = model.Host;
                currentConStr.Password = model.Password;
                currentConStr.Port = model.Port;
            }
            else
            {
                _conStrList.Add(model);
            }
            UpdateConStrList(_conStrList);
        }

        /// <summary>
        /// 构建连接字符串
        /// </summary>
        /// <param name="conStr">连接字符串配置实体</param>
        /// <returns>连接字符串</returns>
        /// <exception cref="Exception">数据库陌生配置异常</exception>
        private static string CreateConStr(ConStr conStr)
        {
            switch (conStr.Category)
            {
                case DbStyle.SqlServer:
                    return CreateSqlServerConString(conStr);
                case DbStyle.Sqlite:
                    return CreateSqliteConString(conStr);
                case DbStyle.Postgres:
                    return CreatePostgresConString(conStr);
                case DbStyle.MySql:
                    return CreateMySqlConString(conStr);
                case DbStyle.Oracle:
                    return CreateOracleConString(conStr);
                case DbStyle.Access:
                    return CreateAccessConString(conStr);
                default:
                    throw new Exception(string.Format("不支持的数据库类型：{0}", conStr.Category.ToString()));
            }
        }

        /// <summary>
        /// 构建sqlite连接字符串
        /// </summary>
        /// <param name="conStr">连接字符串配置实体</param>
        /// <returns>sqlite连接字符串</returns>
        private static string CreateSqliteConString(ConStr conStr)
        {
            ValidFilePath(conStr.FilePath);
            return string.Format("Data Source={0};Version=3;", conStr.FilePath);
        }

        /// <summary>
        /// 构建postgres连接字符串
        /// </summary>
        /// <param name="conStr">连接字符串配置实体</param>
        /// <returns>postgres连接字符串</returns>
        private static string CreatePostgresConString(ConStr conStr)
        {
            ValidHost(conStr.Host);
            ValidPort(conStr.Port);
            ValidUser(conStr.User);
            ValidPassword(conStr.Password);
            ValidDbName(conStr.DbName);
            return string.Format("Server={0};Port={1};UserId={2};Password={3};Database={4};", conStr.Host, conStr.Port.ToString(), conStr.User, conStr.Password, conStr.DbName);
        }

        /// <summary>
        /// 构建sqlServer连接字符串
        /// </summary>
        /// <param name="conStr">连接字符串配置实体</param>
        /// <returns>sqlServer连接字符串</returns>
        private static string CreateSqlServerConString(ConStr conStr)
        {
            ValidHost(conStr.Host);
            ValidInstance(conStr.Instance);
            ValidDbName(conStr.DbName);
            /*
            "Server=myServerAddress;Database=myDataBase;Trusted_Connection=True;";
            "Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password=myPassword;";
             */
            if (conStr.SysTrusted)
            {
                return string.Format("Data Source={0}\\{1};Initial Catalog={2};Trusted_Connection=True;", conStr.Host, conStr.Instance, conStr.DbName);
            }
            else
            {
                ValidUser(conStr.User);
                ValidPassword(conStr.Password);
                return string.Format("Data Source={0}\\{1};Initial Catalog={2};Persist Security Info=True;TrustServerCertificate=True;User ID={3};Password ={4};", conStr.Host, conStr.Instance, conStr.DbName, conStr.User, conStr.Password);
            }
        }

        /// <summary>
        /// 构建mySql连接字符串
        /// </summary>
        /// <param name="conStr">连接字符串配置实体</param>
        /// <returns>mySql连接字符串</returns>
        private static string CreateMySqlConString(ConStr conStr)
        {
            ValidHost(conStr.Host);
            ValidPort(conStr.Port);
            ValidDbName(conStr.DbName);
            ValidUser(conStr.User);
            ValidPassword(conStr.Password);
            return string.Format("Server={0};Port={1};Database={2};Uid={3};Pwd={4};", conStr.Host, conStr.Port.ToString(), conStr.DbName, conStr.User, conStr.Password);
        }

        /// <summary>
        /// 构建oracle连接字符串
        /// </summary>
        /// <param name="conStr">连接字符串配置实体</param>
        /// <returns>oracle连接字符串</returns>
        private static string CreateOracleConString(ConStr conStr)
        {
            ValidHost(conStr.Host);
            ValidPort(conStr.Port);
            ValidDbName(conStr.DbName);
            ValidUser(conStr.User);
            ValidPassword(conStr.Password);
            return string.Format("User Id={3};Password={4};Server={0}:{1}/{2};", conStr.Host, conStr.Port.ToString(), conStr.DbName, conStr.User, conStr.Password);
        }

        /// <summary>
        /// 构建access连接字符串
        /// </summary>
        /// <param name="conStr">连接字符串配置实体</param>
        /// <returns>access连接字符串</returns>
        private static string CreateAccessConString(ConStr conStr)
        {
            ValidFilePath(conStr.FilePath);

            if (conStr.Version <= 2003)
            {
                return string.Format("Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};", conStr.FilePath);
            }
            else
            {
                return string.Format("Provider=Microsoft.ACE.OLEDB.12.0;Data Source={0};", conStr.FilePath);
            }
        }

        /// <summary>
        /// 验证主机
        /// </summary>
        /// <param name="host">主机</param>
        /// <exception cref="Exception">数据库主机配置异常</exception>
        private static void ValidHost(string host)
        {
            if (host != "localhost" && !StringHelper.RegexIsIP(host))
            {
                throw new Exception("数据库主机配置错误");
            }
        }

        /// <summary>
        /// 验证数据库实例
        /// </summary>
        /// <param name="instance">实例信息</param>
        /// <exception cref="Exception">数据库实例信息配置异常</exception>
        private static void ValidInstance(string instance)
        {
            if (string.IsNullOrEmpty(instance))
            {
                throw new Exception("数据库实例信息配置错误");
            }
        }

        /// <summary>
        /// 验证数据库端口
        /// </summary>
        /// <param name="port">端口</param>
        /// <exception cref="Exception">数据库端口配置异常</exception>
        private static void ValidPort(int port)
        {
            if (port <= 0 || port > 65535)
            {
                throw new Exception("数据库端口配置错误");
            }
        }

        /// <summary>
        /// 验证数据库用户
        /// </summary>
        /// <param name="user">用户</param>
        /// <exception cref="Exception">数据库用户信息配置异常</exception>
        private static void ValidUser(string user)
        {
            if (string.IsNullOrEmpty(user))
            {
                throw new Exception("数据库用户信息配置错误");
            }
        }
        /// <summary>
        /// 验证数据库密码配置
        /// </summary>
        /// <param name="pwd">密码</param>
        /// <exception cref="Exception">数据库密码配置</exception>
        private static void ValidPassword(string pwd)
        {
            if (string.IsNullOrEmpty(pwd))
            {
                throw new Exception("数据库密码配置错误");
            }
        }

        /// <summary>
        /// 验证数据库目标
        /// </summary>
        /// <param name="dbName">目标</param>
        /// <exception cref="Exception">数据库目标配置</exception>
        private static void ValidDbName(string dbName)
        {
            if (string.IsNullOrEmpty(dbName))
            {
                throw new Exception("数据库目标配置错误");
            }
        }

        /// <summary>
        /// 验证数据库文件路径
        /// </summary>
        /// <param name="filePath">文件路径</param>
        /// <exception cref="Exception">数据库文件路径配置异常</exception>
        private static void ValidFilePath(string filePath)
        {
            if (!File.Exists(filePath))
            {
                throw new Exception("数据库文件路径配置错误");
            }
        }


        /// <summary>
        /// 从数据库配置文件获取连接字符串配置实体列表
        /// </summary>
        /// <param name="configPath">数据库配置文件路径</param>
        /// <returns>连接字符串配置实体列表</returns>
        private static List<ConStr> GetConStrList(string configPath = null)
        {
            if (string.IsNullOrEmpty(configPath))
            {
                configPath = _defaultPath;
            }
            List<ConStr> res = new List<ConStr>();
            if (!File.Exists(configPath))
            {
                string fileDir = Path.GetDirectoryName(configPath);
                if (!Directory.Exists(fileDir))
                {
                    Directory.CreateDirectory(fileDir);
                }
            }
            else
            {
                res = XmlHelper.XmlDeserializeList<ConStr>(configPath);
            }
            return res;
        }

        /// <summary>
        /// 更新数据库配置信息
        /// </summary>
        /// <param name="data">数据库配置信息</param>
        /// <param name="configPath">数据库配置文件路径</param>
        private static void UpdateConStrList(List<ConStr> data, string configPath = null)
        {
            if (string.IsNullOrEmpty(configPath))
            {
                configPath = _defaultPath;
            }
            if (!File.Exists(configPath))
            {
                string fileDir = Path.GetDirectoryName(configPath);
                if (!Directory.Exists(fileDir))
                {
                    Directory.CreateDirectory(fileDir);
                }
            }
            XmlHelper.XmlSerialize(data, configPath);
        }

        /// <summary>
        /// 连接字符串缓存
        /// </summary>
        private static List<ConStr> _conStrList = new List<ConStr>();
        /// <summary>
        /// 默认连接对象序列化后存储的位置
        /// </summary>
        private static string _defaultPath = AppDomain.CurrentDomain.BaseDirectory + "Config\\DbConfig.xml";
    }
}
